Skip to content

feat(marketplace): expose github.clientId from discovery + harden publish auth#271

Open
masonjames wants to merge 2 commits intoemdash-cms:mainfrom
masonjames:marketplace-publish-auth
Open

feat(marketplace): expose github.clientId from discovery + harden publish auth#271
masonjames wants to merge 2 commits intoemdash-cms:mainfrom
masonjames:marketplace-publish-auth

Conversation

@masonjames
Copy link
Copy Markdown
Contributor

@masonjames masonjames commented Apr 5, 2026

What does this PR do?

Hardens the marketplace GitHub auth discovery/publish flow so partial or missing OAuth configuration fails clearly instead of advertising a broken device-login path.

It also adds the email:status hook to core typing/manifest validation, updates the deploy smoke check to require github.enabled === true, and adds tests covering the discovery and manifest changes.

Type of change

  • Bug fix
  • Feature (requires approved Discussion)
  • Refactor (no behavior change)
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code

Screenshots / test output

  • pnpm --silent lint:quick
  • pnpm --filter @emdash-cms/marketplace exec vitest run tests/publish-e2e.test.ts tests/manifest-schema.test.ts
  • pnpm --filter emdash exec vitest run tests/unit/cli/publish.test.ts tests/unit/plugins/define-plugin.test.ts tests/unit/plugins/manifest-schema.test.ts tests/unit/plugins/hooks.test.ts
  • pnpm typecheck
  • pnpm --silent lint:json | jq '.diagnostics | length'

Copilot AI review requested due to automatic review settings April 5, 2026 14:40
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 5, 2026

🦋 Changeset detected

Latest commit: 76f8525

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates EmDash’s marketplace auth/discovery and publish flow so the CLI can discover the GitHub OAuth app at runtime, while also tightening failure behavior when marketplace auth is misconfigured. It also introduces a new email:status plugin hook type/schema entry and adds targeted unit/e2e coverage.

Changes:

  • Expose github.clientId from GET /api/v1/auth/discovery and harden marketplace GitHub auth routes to return clear 503s when OAuth config is missing.
  • Fix the CLI GitHub Device Flow requests to use application/x-www-form-urlencoded, and add unit tests for encoding + redirect middleware DB fallback.
  • Add email:status to core hook typing, hook pipeline capability gating, and manifest validation; update docs + deploy workflow accordingly.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/marketplace/wrangler.jsonc Documents required Worker secrets for production deploys.
packages/marketplace/tests/publish-e2e.test.ts Adds e2e assertions for discovery payload and 503 behavior when OAuth config is missing.
packages/marketplace/src/routes/public.ts Returns discovery payload including github.clientId.
packages/marketplace/src/routes/author.ts Adds shared GitHub OAuth config validation + clearer 503 errors for misconfigurations.
packages/marketplace/README.md Documents production deploy secret requirements and a post-deploy discovery check.
packages/core/tests/unit/redirects/middleware.test.ts Adds unit coverage for redirect middleware DB fallback behavior.
packages/core/tests/unit/plugins/manifest-schema.test.ts Ensures manifests accept the new email:status hook.
packages/core/tests/unit/plugins/define-plugin.test.ts Ensures definePlugin resolves/retains email:status hooks.
packages/core/tests/unit/cli/publish.test.ts Adds unit test for form-urlencoded encoding used by GitHub device flow.
packages/core/src/plugins/types.ts Introduces EmailStatusEvent + EmailStatusHandler and registers the hook in types.
packages/core/src/plugins/manifest-schema.ts Adds email:status to manifest hook allowlist.
packages/core/src/plugins/hooks.ts Registers email:status in the pipeline and gates it behind email:provide.
packages/core/src/cli/commands/publish.ts Uses discovery github.clientId, fails fast if missing, and switches GitHub device flow to form-urlencoded.
packages/core/src/astro/middleware/redirect.ts Falls back to getDb() when locals.emdash isn’t available for anonymous requests.
docs/src/content/docs/reference/configuration.mdx Updates plugin configuration guidance and “paired trusted/marketplace” guidance.
docs/src/content/docs/plugins/sandbox.mdx Adds guidance on paired trusted + marketplace packages.
docs/src/content/docs/plugins/publishing.mdx Documents paired package strategy for trusted vs sandbox-safe distributions.
docs/src/content/docs/plugins/installing.mdx Adds tip about paired trusted + marketplace packages.
.github/workflows/deploy-marketplace.yml Validates required secrets, pushes Worker secrets, deploys Worker, and smoke-checks discovery before seeding plugins.
.changeset/smooth-bugs-wave.md Adds a patch changeset describing the hook + marketplace auth hardening.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 16 to 21
publicRoutes.get("/auth/discovery", (c) => {
const clientId = typeof c.env.GITHUB_CLIENT_ID === "string" ? c.env.GITHUB_CLIENT_ID : "";
return c.json({
github: {
clientId: c.env.GITHUB_CLIENT_ID,
clientId,
deviceAuthorizationEndpoint: "https://github.com/login/device/code",
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/auth/discovery returns clientId without trimming. If the Worker secret contains trailing whitespace/newlines (common when copy/pasting), discovery will expose a non-empty but invalid clientId, and the CLI will proceed until GitHub rejects the request. Consider .trim() here (matching getGitHubOAuthConfig() in author routes) so whitespace-only values become an empty string.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@ascorbic
Copy link
Copy Markdown
Collaborator

Could you explan a bit more the rationale behind these?

Expose the marketplace GitHub client ID via discovery only when OAuth is fully configured, fail missing OAuth config clearly, align CLI device-flow requests with GitHub's form-encoded API, and add email:status hook/schema support for email provider readiness.
@masonjames masonjames force-pushed the marketplace-publish-auth branch from 61d42b5 to 27feba6 Compare April 13, 2026 14:22
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 13, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@271

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@271

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@271

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@271

emdash

npm i https://pkg.pr.new/emdash@271

create-emdash

npm i https://pkg.pr.new/create-emdash@271

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@271

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@271

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@271

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@271

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@271

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@271

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@271

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@271

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@271

commit: 76f8525

@masonjames
Copy link
Copy Markdown
Contributor Author

Yeah, happy to add more context here.

This came out of the work to get a real SMTP/Resend provider working, plus the follow-on work to make the marketplace publish path usable. The SMTP plugin highlighted two separate but related gaps:

  1. Marketplace publish auth. The CLI needs a public GitHub clientId from marketplace discovery to start device auth, but the marketplace needs both OAuth secrets configured to complete the token exchange. Before this, a partially configured deployment could advertise a login path that would only fail later and less clearly. The discovery change makes auth availability explicit, the author routes return a clear 503 when the deployment is missing OAuth config, and the CLI fails early with an actionable message. The form-encoded request body is just aligning the device-flow calls with GitHub’s expected API shape.

  2. Email provider readiness. For SMTP/Resend-style providers, we need a small email:status hook so EmDash can ask the selected email provider whether it’s configured and ready to send. The type, runtime hook registration, capability gate, and manifest schemas all move together so trusted plugins and marketplace/sandboxed plugin manifests agree on the same hook contract.

  3. Trusted vs marketplace package docs/deploy guardrails. The SMTP plugin is a good example of why a plugin may need a trusted package (emdash-smtp, with server-only/provider code and secrets) and a sandbox-safe marketplace package (emdash-smtp-marketplace). The docs call that pattern out, and the deploy workflow changes are meant to prevent us from deploying/seeding a marketplace that reports broken auth discovery because required Worker secrets were missing.

So the short version is: this PR is trying to upstream the pieces the SMTP/Resend work exposed — not the SMTP plugin itself, but the core/marketplace support needed for email providers and publishing. I’m happy to split the marketplace auth hardening and the email:status API addition into separate PRs if you’d prefer that shape.

Direct repo link: https://github.com/masonjames/emdash-smtp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants